package speed2010.andreza.vspeed.view;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;

import prefuse.Display;
import speed2010.andreza.vspeed.integration.CommunicationManager;
import speed2010.andreza.vspeed.integration.InvokerGeneric;
import speed2010.andreza.vspeed.integration.NodeGeneric;
import speed2010.application.gui.VirtualNodeInfo;
import speed2010.middleware.comunicacao.Address;

/**
 * Classe utilizada para construir a topologia da rede.
 */

public class ConsolidadorInfoPrefuse {
	private final String selectedValue;

	public ConsolidadorInfoPrefuse(final String selectValue) {
		this.selectedValue = selectValue;
	}

	/**
	 * Generated the topolyg display
	 * 
	 * @param mapTopology
	 * @param showPD
	 * @param showPI
	 * @return
	 */
	public Display gerarDisplay(final Hashtable<String, VirtualNodeInfo> mapTopology, final boolean showPD,
			final boolean showPI) {
		final Hashtable<Integer, String> ident_tipo = new Hashtable<Integer, String>();
		final Hashtable<Integer, String> ident_name = new Hashtable<Integer, String>();
		final Hashtable<Integer, Integer> ps_ps = new Hashtable<Integer, Integer>();
		final Hashtable<Integer, Integer> pd_pi = new Hashtable<Integer, Integer>();
		final Hashtable<Integer, Integer> pi_ps = new Hashtable<Integer, Integer>();

		try {

			// Declaracao dos Peers
			for (final String nodeAddressReduzido : mapTopology.keySet()) {
				final Address nodeAddress = new Address(nodeAddressReduzido);
				final VirtualNodeInfo nodeInfoTemp = mapTopology.get(nodeAddress.toStringReduzido());
				String name = "";
				String tipo = "";
				if (nodeInfoTemp == null) {
					throw new Exception("BUG");
				} else {
					switch (nodeInfoTemp.getNodeType()) {
						case PEERSEMANTICO :
							name = nodeInfoTemp.getDominio();
							tipo = nodeInfoTemp.getIdentificador() + " - PS - " + nodeInfoTemp.getAddress().getPorta();
							break;
						case PEERDEDADOS :
							tipo = "PD";
							name = nodeInfoTemp.getAddress().getPorta() + "";
							break;
						case PEERDEINTEGRACAO :
							tipo = "PI";
							name = nodeInfoTemp.getAddress().getPorta() + "";
							break;
						default :
							System.err.println("Erro: Nenhuma opção selecionada");
							break;
					}
				}

				ident_tipo.put(nodeInfoTemp.getAddress().getPorta(), tipo);
				ident_name.put(nodeInfoTemp.getAddress().getPorta(), name);
			}

			// faz a ligação dos nós declarados
			for (final String nodeAddressReduzido : mapTopology.keySet()) {
				final Address nodeAddress = new Address(nodeAddressReduzido);
				final VirtualNodeInfo sourceNodeInfoTemp = mapTopology.get(nodeAddress.toStringReduzido());

				final String source = sourceNodeInfoTemp.getAddress().getPorta() + "";

				switch (sourceNodeInfoTemp.getNodeType()) {
					case PEERSEMANTICO :

						final int idPredecessor = sourceNodeInfoTemp.getIdPredecessor();
						if (idPredecessor != -1) {
							String target = null;
							try { // O target deveria ser o predecessor

								final NodeGeneric nodeGeneric = CommunicationManager.getInstance().getGenericPeer();
								final InvokerGeneric genericInvoker = nodeGeneric.getInvokerG();
								final Address addressPredecessor = genericInvoker.getAddress(idPredecessor);

								final VirtualNodeInfo targetNodeInfo = mapTopology.get(addressPredecessor
										.toStringReduzido());
								if (targetNodeInfo != null) {
									target = targetNodeInfo.getAddress().getPorta() + "";
								} else {
									System.err.println("Erro: targetNodeInfo " + targetNodeInfo + " está NULL");
								}

							} catch (final Exception e) {
								e.printStackTrace();
							}
							if ((target != null) && (source != null)) {
								ps_ps.put(Integer.parseInt(source), Integer.parseInt(target));
							}
						} else {
							new Exception("Erro: um PS não pode ter um id predecessor = -1");
						}
						break;
					case PEERDEDADOS :
						if (sourceNodeInfoTemp.getParentNodeAddress() != null) {
							final String target_PI = sourceNodeInfoTemp.getParentNodeAddress().getPorta() + "";
							pd_pi.put(Integer.parseInt(source), Integer.parseInt(target_PI));
						}
						break;
					case PEERDEINTEGRACAO :
						if (sourceNodeInfoTemp.getParentNodeAddress() != null) {
							final String target_PS = sourceNodeInfoTemp.getParentNodeAddress().getPorta() + "";
							pi_ps.put(Integer.parseInt(source), Integer.parseInt(target_PS));
						}
						break;
				}
			}
		} catch (final Exception e) {
			e.printStackTrace();
		}

		if (!showPI) {
			for (final Entry<Integer, Integer> entry : pi_ps.entrySet()) {
				ident_tipo.remove(entry.getKey());
				ident_name.remove(entry.getKey());
			}
			pi_ps.clear();

			for (final Entry<Integer, Integer> entry : pd_pi.entrySet()) {
				ident_tipo.remove(entry.getKey());
				ident_name.remove(entry.getKey());
			}
			pd_pi.clear();
		}

		if (!showPD) {
			for (final Entry<Integer, Integer> entry : pd_pi.entrySet()) {
				ident_tipo.remove(entry.getKey());
				ident_name.remove(entry.getKey());
			}
			pd_pi.clear();
		}

		final Display d = new PrefuseAggregate(this.selectedValue, ident_tipo, ident_name, ps_ps, pd_pi, pi_ps);

		return d;
	}

	public Display updateDisplay(final Hashtable<String, VirtualNodeInfo> mapTopology, final String domain,
			final boolean b, final boolean c) {
		final Hashtable<Integer, String> ident_tipo = new Hashtable<Integer, String>();
		final Hashtable<Integer, String> ident_name = new Hashtable<Integer, String>();
		final Hashtable<Integer, Integer> ps_ps = new Hashtable<Integer, Integer>();
		final Hashtable<Integer, Integer> pd_pi = new Hashtable<Integer, Integer>();
		final Hashtable<Integer, Integer> pi_ps = new Hashtable<Integer, Integer>();

		try {
			// Declaracao dos Peers
			for (final String nodeAddressReduzido : mapTopology.keySet()) {
				final Address nodeAddress = new Address(nodeAddressReduzido);
				final VirtualNodeInfo nodeInfoTemp = mapTopology.get(nodeAddress.toStringReduzido());
				String name = "";
				String tipo = "";
				if (nodeInfoTemp == null) {
					throw new Exception("BUG");
				} else {
					switch (nodeInfoTemp.getNodeType()) {
						case PEERSEMANTICO :
							name = nodeInfoTemp.getDominio();
							tipo = nodeInfoTemp.getIdentificador() + " - PS - " + nodeInfoTemp.getAddress().getPorta();
							break;
						case PEERDEDADOS :
							tipo = "PD";
							name = nodeInfoTemp.getAddress().getPorta() + "";
							break;
						case PEERDEINTEGRACAO :
							tipo = "PI";
							name = nodeInfoTemp.getAddress().getPorta() + "";
							break;
						default :
							System.err.println("Erro: Nenhuma opção selecionada");
							break;
					}
				}

				ident_tipo.put(nodeInfoTemp.getAddress().getPorta(), tipo);
				ident_name.put(nodeInfoTemp.getAddress().getPorta(), name);
			}

			// faz a ligação dos nós declarados
			for (final String nodeAddressReduzido : mapTopology.keySet()) {
				final Address nodeAddress = new Address(nodeAddressReduzido);
				final VirtualNodeInfo sourceNodeInfoTemp = mapTopology.get(nodeAddress.toStringReduzido());

				final String source = sourceNodeInfoTemp.getAddress().getPorta() + "";

				switch (sourceNodeInfoTemp.getNodeType()) {
					case PEERSEMANTICO :

						final int idPredecessor = sourceNodeInfoTemp.getIdPredecessor();
						if (idPredecessor != -1) {
							String target = null;
							try { // O target deveria ser o predecessor

								final NodeGeneric nodeGeneric = CommunicationManager.getInstance().getGenericPeer();
								final InvokerGeneric genericInvoker = nodeGeneric.getInvokerG();
								final Address addressPredecessor = genericInvoker.getAddress(idPredecessor);

								final VirtualNodeInfo targetNodeInfo = mapTopology.get(addressPredecessor
										.toStringReduzido());
								if (targetNodeInfo != null) {
									target = targetNodeInfo.getAddress().getPorta() + "";
								} else {
									System.err.println("Erro: targetNodeInfo " + targetNodeInfo + " está NULL");
								}

							} catch (final Exception e) {
								e.printStackTrace();
							}
							if ((target != null) && (source != null)) {
								ps_ps.put(Integer.parseInt(source), Integer.parseInt(target));
							}
						} else {
							new Exception("Erro: um PS não pode ter um id predecessor = -1");
						}
						break;
					case PEERDEDADOS :
						if (sourceNodeInfoTemp.getParentNodeAddress() != null) {
							final String target_PI = sourceNodeInfoTemp.getParentNodeAddress().getPorta() + "";
							pd_pi.put(Integer.parseInt(source), Integer.parseInt(target_PI));
						}
						break;
					case PEERDEINTEGRACAO :
						if (sourceNodeInfoTemp.getParentNodeAddress() != null) {
							final String target_PS = sourceNodeInfoTemp.getParentNodeAddress().getPorta() + "";
							pi_ps.put(Integer.parseInt(source), Integer.parseInt(target_PS));
						}
						break;
				}
			}
		} catch (final Exception e) {
			e.printStackTrace();
		}

		int domainPort = -1;
		for (final Entry<Integer, String> entry : ident_name.entrySet()) {
			if (entry.getValue().equals(domain)) {
				domainPort = entry.getKey();
			}
		}

		final Set<Integer> removedPS = new HashSet<Integer>();
		for (final Iterator<Entry<Integer, Integer>> iterator = ps_ps.entrySet().iterator(); iterator.hasNext();) {
			final Entry<Integer, Integer> entry = iterator.next();
			if ((entry.getKey() == domainPort) || (entry.getValue() == domainPort)) {
				removedPS.add(entry.getKey());
				iterator.remove();
			}
		}
		removedPS.remove(domainPort);

		final Set<Integer> removedPI = new HashSet<Integer>();
		for (final Iterator<Entry<Integer, Integer>> iterator = pi_ps.entrySet().iterator(); iterator.hasNext();) {
			final Entry<Integer, Integer> entry = iterator.next();
			if (entry.getValue() != domainPort) {
				removedPI.add(entry.getKey());
				iterator.remove();
			}
		}

		final Set<Integer> removedPD = new HashSet<Integer>();
		for (final Iterator<Entry<Integer, Integer>> iterator = pd_pi.entrySet().iterator(); iterator.hasNext();) {
			final Entry<Integer, Integer> entry = iterator.next();
			if (removedPI.contains(entry.getValue())) {
				removedPD.add(entry.getKey());
				iterator.remove();
			}
		}

		for (final Iterator<Entry<Integer, String>> iterator = ident_tipo.entrySet().iterator(); iterator.hasNext();) {
			final Entry<Integer, String> entry = iterator.next();

			final boolean containsPS = removedPS.contains(entry.getKey());
			final boolean containsPI = removedPI.contains(entry.getKey());
			final boolean containsPD = removedPD.contains(entry.getKey());
			if (containsPS || containsPI || containsPD) {
				iterator.remove();
			}
		}

		for (final Iterator<Entry<Integer, String>> iterator = ident_name.entrySet().iterator(); iterator.hasNext();) {
			final Entry<Integer, String> entry = iterator.next();

			final boolean containsPS = removedPS.contains(entry.getKey());
			final boolean containsPI = removedPI.contains(entry.getKey());
			final boolean containsPD = removedPD.contains(entry.getKey());
			if (containsPS || containsPI || containsPD) {
				iterator.remove();
			}
		}

		final Display d = new PrefuseAggregate(null, ident_tipo, ident_name, ps_ps, pd_pi, pi_ps);

		return d;
	}
}